App.require(["canvasTool", "htmlTool"], function (canvasTool, htmlTool) {
    var app = App.getCurrent();

    app.defineLayer("str-column-chart", {
        render: function (next) {
            this.initProperty();
            this.data.isChange = true;
            this.data.isMouseIn = false;
            this.fetch("./index.html", function (text) {
                next(this.renderHTML(text));
            })
        },
        removeOption: function (index) {
            this.data.list.splice(index, 1);
            this.data.isChange = true;
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        appendOption: function (option) {
            this.data.list.push(option);
            this.data.isChange = true;
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        insertBefore: function (index, option) {
            this.data.list.splice(index, 0, option);
            this.data.isChange = true;
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        changeOption: function (index, opt) {
            var option = this.data.list[index];
            if (!option) return;
            this.data.isChange = true;
            this.addChange(opt, option);
        },
        clickLayerHandler: function (ev, ctx, ratio) {
            var offsetObj = this.createOffsetObj(this.offset, ratio, this._offset);
            var rstart = this.createOffsetObj(this.rstart, ratio, this._rstart);
            var rend = this.createOffsetObj(this.rend, ratio, this._rend);
            var direction = this.direction, cwidth = this.cwidth * ratio, distance = this.distance * ratio;
            var startObj = this._getStartPos(offsetObj, direction, ratio);
            var startX = startObj.x, startY = startObj.y;
            ctx.translate(this.x * ratio, this.y * ratio);
            for (var i = 0; i < this.data.list.length; i++) {
                var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                var obj = this.data.list[i], h = parseInt(obj.value) * ratio;
                this._createPath(ctx, offsetPos.x, offsetPos.y, obj.width ? obj.width * ratio :  cwidth, h, rstart, rend, direction);
                if (ctx.isPointInPath(ev.clientX * ratio, ev.clientY * ratio)) {
                    this.dispatchCustomEvent("columnchartclick", {
                        component: this,
                        event: ev,
                        index: i,
                        data: obj
                    });
                    break;
                }
            }
        },
        moveLayerHandler: function (ev, ctx, ratio) {
            var offsetObj = this.createOffsetObj(this.offset, ratio, this._offset);
            var rstart = this.createOffsetObj(this.rstart, ratio, this._rstart);
            var rend = this.createOffsetObj(this.rend, ratio, this._rend);
            var direction = this.direction, cwidth = this.cwidth * ratio, distance = this.distance * ratio;
            var startObj = this._getStartPos(offsetObj, direction, ratio);
            var startX = startObj.x, startY = startObj.y, isMouseIn = false;
            ctx.translate(this.x * ratio, this.y * ratio);
            for (var i = 0; i < this.data.list.length; i++) {
                var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                var obj = this.data.list[i], h = parseInt(obj.value) * ratio;
                this._createPath(ctx, offsetPos.x, offsetPos.y, obj.width ? obj.width * ratio : cwidth, h, rstart, rend, direction, ratio);
                if (ctx.isPointInPath(ev.clientX * ratio, ev.clientY * ratio)) {
                    isMouseIn = true;
                    if (!this.data.isMouseIn) {
                        this.data.isMouseIn = true;
                        this.dispatchCustomEvent("columnchartmouseenter", {
                            component: this,
                            event: ev,
                            index: i,
                            data: obj
                        });
                        this.data.index = i;
                        break;
                    }
                }
            }

            if (this.data.isMouseIn && !isMouseIn) {
                this.data.isMouseIn = false;
                this.dispatchCustomEvent("columnchartmouseleave", {
                    component: this,
                    event: ev,
                    index: this.data.index,
                    data: this.data.list[this.data.index]
                });
            }
        },
        dispose: function () {
            this.removeAnimation();
            this.data.isChange = true;
            this.data.isMouseIn = false;
        },
        initProperty: function () {
            var property = this.property;

            this.data.attributeStr = this._createPropertyStr(property);
            this.data.list = htmlTool.getOptions(this.slot);
            property.round = typeof property.round === "undefined" ? false : true;
            if (property.animation) this.addPropertyAnimation(property.animation, this);

            this._offset = { x: 0, y: 0 };
            this._rstart = { x: 0, y: 0 };
            this._rend = { x: 0, y: 0 };
        },
        drawSelf: function (ctx, ratio) {
            if (this.hidden || this.data.list.length == 0) return;
            var property = this.property;
            var dObj = this.getPropertyUse(["width", "height", "opacity", "cwidth", "rstart", "rend", "offset", "distance", "x", "y", "percent", "dlength", "shadow", "font", "fill", "stroke"]);
            if (typeof dObj.opacity !== "undefined") ctx.globalAlpha = parseFloat(dObj.opacity);
            ctx.translate(dObj.x * ratio, dObj.y * ratio);
            this.transformView(ctx, ratio);
            var keyToCanvas = canvasTool.keyToCanvas;
            for (var key in property) {
                var ctxKey = keyToCanvas[key];
                if (ctxKey) {
                    if (ctxKey == "fillStyle" || ctxKey == "strokeStyle") continue;
                    else ctx[ctxKey] = this[key];
                }
            }

            if (dObj.linedash) this.useLineDash(ctx, dObj.linedash, dObj.linedashoffset, ratio);
            if (dObj.shadow) this.useShadow(ctx, dObj.shadow, ratio);
            var offsetObj = this.createOffsetObj(dObj.offset, ratio, this._offset);
            var rstart = this.createOffsetObj(dObj.rstart, ratio, this._rstart);
            var rend = this.createOffsetObj(dObj.rend, ratio, this._rend);
            var direction = this.direction, cwidth = dObj.cwidth * ratio, dlength = dObj.dlength * ratio, distance = dObj.distance * ratio;
            var startObj = this._getStartPos(offsetObj, direction, ratio), percent = dObj.percent;
            var startX = startObj.x, startY = startObj.y;
            for (var i = 0; i < this.data.list.length; i++) {
                var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                var obj = this.data.list[i], h = parseInt(obj.value) * ratio * percent;
                ctx.save();
                if (obj.shadow) this.useShadow(ctx, obj.shadow, ratio);
                if (obj.fill || this.fill) {
                    ctx.save();
                    this._createPath(ctx, offsetPos.x, offsetPos.y, obj.width ? obj.width * ratio : cwidth, h, rstart, rend, direction, ratio);
                    if (obj.shadow || dObj.shadow) {
                        ctx.fillStyle = "rgba(255, 255, 255, 0)";
                        ctx.fill();
                    }
                    ctx.fillStyle = this.getFillOrStrokeStyle(obj.fill || dObj.fill);
                    ctx.clip();
                    this._createFillPath(ctx, offsetPos, h, dlength, cwidth, direction);
                    ctx.fill();
                    ctx.restore();
                }
                if (obj.stroke || dObj.stroke) {
                    ctx.strokeStyle = this.getFillOrStrokeStyle(obj.stroke || dObj.stroke);
                    this._createPath(ctx, offsetPos.x, offsetPos.y, obj.width || cwidth, h, rstart, rend, direction, ratio);
                    ctx.stroke();
                }
                if (obj.marker || this.marker) {
                    var marker = this.getMarkerByStr(obj.marker || this.marker, this, ratio);
                    if (marker) {
                        ctx.save();
                        var markerPos = this._getMarkerPos(offsetPos.x, offsetPos.y, h, direction);
                        ctx.translate(markerPos.x, markerPos.y);
                        marker.drawToCanvas(ctx, ratio);
                        ctx.restore();
                    }
                }
                if (this.mark) {
                    ctx.textAlign = obj.align || "center";
                    ctx.textBaseline = obj.baseline || "middle";
                    if (obj.font || dObj.font) ctx.font = this.computeFont(obj.font || dObj.font, ratio);
                    if (obj.textfill) ctx.fillStyle = this.getFillOrStrokeStyle(obj.textfill || dObj.fill);
                    var offsetTextPos = this._getOffsetTextPos(ctx, offsetPos, obj, h, direction, ratio);
                    ctx.fillText(obj.content, offsetTextPos.x, offsetTextPos.y);
                }
                ctx.restore();
            }
        },
        _getOffsetPos: function (startX, startY, distance, i, direction) {
            switch (direction) {
                case "row": 
                case "row-reverse": 
                    return {
                        x: startX + i * distance, y: startY
                    };
                case "column": 
                case "column-reverse": return {
                    x: startX, y: startY + i * distance
                }
            }
        },
        _getOffsetTextPos: function (ctx, pos, obj, h, direction, ratio) {
            switch (direction) {
                case "row":
                    var ox = obj.offsetx || 0, oy = obj.offsety || -10 * ratio;
                    return { x: pos.x + ox, y:pos.y - h + oy };
                case "row-reverse":
                    var ox = obj.offsetx || 0, oy = obj.offsety || 10 * ratio;
                    return { x: pos.x + ox, y:pos.y + h + oy };
                case "column":
                    var ox = obj.offsetx || 10 * ratio, oy = obj.offsety || 0;
                    return { x: pos.x + h + ox + ctx.measureText(obj.content).width / 2, y: oy + pos.y };
                case "column-reverse":
                    var ox = obj.offsetx || -10 * ratio, oy = obj.offsety || 0;
                    return { x: pos.x - h + ox - ctx.measureText(obj.content).width / 2, y: oy + pos.y };
            }
        },
        _createFillPath: function (ctx, offsetPos, h, dlength, cwidth, direction) {
            switch (direction) {
                case "row":
                case "row-reverse":
                    var he = direction == "row" ? -h : h;
                    ctx.translate(offsetPos.x, offsetPos.y);
                    ctx.scale(1, he / dlength);
                    ctx.beginPath();
                    ctx.rect(-cwidth / 2, 0, cwidth, dlength);
                    break;
                case "column":
                case "column-reverse":
                    var he = direction == "column" ? h : -h;
                    ctx.translate(offsetPos.x, offsetPos.y);
                    ctx.scale(he / dlength, 1);
                    ctx.beginPath();
                    ctx.rect(0, -cwidth / 2, dlength, cwidth);
            }
        },
        _getStartPos: function (offsetObj, direction, ratio) {
            switch (direction) {
                case "row": return {
                    x:  offsetObj.x,
                    y: this.height * ratio - offsetObj.y
                };
                case "row-reverse": 
                case "column":
                    return {
                        x: offsetObj.x,
                        y: offsetObj.y
                    };
                case "column-reverse":
                    return {
                        x: -offsetObj.x + this.width * ratio,
                        y: offsetObj.y
                    };
            }
        },
        _getMarkerPos: function (sx, sy, height, direction) {
            switch (direction) {
                case "row": return {
                    x: sx , y: sy - height
                };
                case "row-reverse": return {
                    x: sx, y: sy + height
                };
                case "column": return {
                    x: sx + height, y: sy
                };
                case "column-reverse": return {
                    x: sx - height, y: sy
                }
            }
        },
        _createPath: function (ctx, sx, sy, width, height, rs, re, direction, ratio) {
            ratio = ratio || 1;
            var rsx = Math.min(parseInt(rs.x), width), 
                rsy = Math.min(parseInt(rs.y), width), 
                rex = Math.min(parseInt(re.x), width), 
                rey = Math.min(parseInt(re.y), width),
                round = this.round;
            ctx.beginPath();
            if (direction === "row") {
                if (round && rsx && rsy)  {
                    ctx.moveTo(sx - width / 2 + rsx, sy);
                    ctx.ellipse(sx - width / 2 + rsx, sy - rsy, rsx, rsy, 0, Math.PI / 2, Math.PI);
                }
                else {
                    ctx.moveTo(sx - width / 2, sy);
                }
                if (rsx && rsy) {
                    ctx.lineTo(sx - width / 2, sy - height + rsy);
                    ctx.ellipse(sx - width / 2 + rsx, sy - height + rsy, rsx, rsy, 0, Math.PI, 3 * Math.PI / 2);
                }
                else {
                    ctx.lineTo(sx - width / 2, sy - height);
                }

                if (rex && rey) {
                    ctx.lineTo(sx + width / 2 - rex, sy - height);
                    ctx.ellipse(sx + width / 2 - rex, sy - height + rey, rex, rey, 0, -Math.PI / 2, 0);
                }
                else {
                    ctx.lineTo(sx + width / 2, sy - height);
                }
                if (round && rex && rey) {
                    ctx.lineTo(sx + width / 2, sy);
                    ctx.ellipse(sx + width / 2 - rex, sy - rey, rex, rey, 0, 0, Math.PI / 2);
                }
                else {
                    ctx.lineTo(sx + width / 2, sy);
                }
            }
            else if (direction === "row-reverse") {
                ctx.moveTo(sx - width / 2, sy);
                if (rsx && rsy) {
                    ctx.lineTo(sx - width / 2, sy + height - rsy);
                    ctx.ellipse(sx - width / 2 + rsx, sy + height - rsy, rsx, rsy, 0, Math.PI, Math.PI / 2, true);
                }
                else {
                    ctx.lineTo(sx - width / 2, sy + height);
                }

                if (rex && rey) {
                    ctx.lineTo(sx + width / 2 - rex, sy + height);
                    ctx.ellipse(sx + width / 2 - rex, sy + height - rey, rex, rey, 0, Math.PI / 2, 0, true);
                }
                else {
                    ctx.lineTo(sx + width / 2, sy - height);
                }
                ctx.lineTo(sx + width / 2, sy);
            }
            else if (direction === "column") {
                ctx.moveTo(sx, sy - width / 2);
                if (rsx && rsy) {
                    ctx.lineTo(sx + height - rsx, sy - width / 2);
                    ctx.ellipse(sx + height - rsx, sy - width / 2 + rsy, rsx, rsy, 0, -Math.PI / 2, 0);
                }
                else {
                    ctx.lineTo(sx + height, sy - width / 2);
                }

                if (rex && rey) {
                    ctx.lineTo(sx + height, sy + width / 2 - rey);
                    ctx.ellipse(sx + height - rsx, sy + width / 2 - rey, rex, rey, 0, 0, Math.PI / 2);
                }
                else {
                    ctx.lineTo(sx + height, sy + width / 2);
                }
                ctx.lineTo(sx, sy + width / 2);
            }
            else if (direction === "column-reverse") {
                ctx.moveTo(sx, sy - width / 2);
                if (rsx && rsy) {
                    ctx.lineTo(sx - height + rsx, sy - width / 2);
                    ctx.ellipse(sx - height + rsx, sy - width / 2 + rsy, rsx, rsy, 0, -Math.PI / 2, -Math.PI, true);
                }
                else {
                    ctx.lineTo(sx - height, sy - width / 2);
                }

                if (rex && rey) {
                    ctx.lineTo(sx - height, sy + width / 2 - rey);
                    ctx.ellipse(sx - height + rsx, sy + width / 2 - rey, rex, rey, 0, -Math.PI, -3 * Math.PI / 2, true);
                }
                else {
                    ctx.lineTo(sx - height, sy + width / 2);
                }
                ctx.lineTo(sx, sy + width / 2);
            }
        },
        set round(value) {
            this.setValue("round", value);
        },
        get round() {
            return this.getValue("round");
        },
        set height(value) {
            this.setValue("height", value);
        },
        get height() {
            return this.getValue("height");
        },
        set width(value) {
            this.setValue("width", value);
        },
        get width() {
            return this.getValue("width");
        },
        get direction() {
            return this.getValue("direction");
        },
        set direction(value) {
            this.setValue("direction", value);
        },
        get marker() {
            return this.getValue("marker");
        },
        set marker(value) {
            this.setValue("marker", value);
        },
        get cwidth() {
            return this.getValue("cwidth");
        },
        set cwidth(value) {
            this.setValue("cwidth", value);
        },
        get distance() {
            return this.getValue("distance");
        },
        set distance(value) {
            this.setValue("distance", value);
        },
        get rstart() {
            return this.getValue("rstart");
        },
        set rstart(value) {
            this.setValue("rstart", value);
        },
        get rend() {
            return this.getValue("rend");
        },
        set rend(value) {
            this.setValue("rend", value);
        },
        get offset() {
            return this.getValue("offset");
        },
        set offset(value) {
            this.setValue("offset", value);
        },
        get fill() {
            return this.getValue("fill");
        },
        set fill(value) {
            this.setValue("fill", value);
        },
        get stroke() {
            return this.getValue("stroke");
        },
        set stroke(value) {
            this.setValue("stroke", value);
        },
        get linedash() {
            return this.getValue("linedash(");
        },
        set linedash(value) {
            this.setValue("linedash(", value);
        },
        get linedashoffset() {
            return this.getValue("linedashoffset");
        },
        set linedashoffset(value) {
            this.setValue("linedashoffset", value);
        },
        get linecap() {
            return this.getValue("linecap");
        },
        set linecap(value) {
            this.setValue("linecap", value);
        },
        get linejoin() {
            return this.getValue("linejoin");
        },
        set linejoin(value) {
            this.setValue("linejoin", value);
        },
        get linewidth() {
            return this.getValue("linewidth");
        },
        set linewidth(value) {
            this.setValue("linewidth", value);
        },
        get miterlimit() {
            return this.getValue("miterlimit");
        },
        set miterlimit(value) {
            this.setValue("miterlimit", value);
        },
        get transform() {
            return this.getValue("transform");
        },
        set transform(value) {
            this.setValue("transform", value);
        },
        get shadow() {
            return this.getValue("shadow");
        },
        set shadow(value) {
            this.setValue("shadow", value);
        },
        get hidden() {
            return this.getValue("hidden");
        },
        set hidden(value) {
            this.setValue("hidden", value);
        },
        get x() {
            return this.getValue("x");
        },
        set x(value) {
            this.setValue("x", value);
        },
        get y() {
            return this.getValue("y");
        },
        set y(value) {
            this.setValue("y", value)
        },
        get percent() {
            return this.getValue("percent");
        },
        set percent(value) {
            this.setValue("percent", value)
        },
        get dlength() {
            return this.getValue("dlength");
        },
        set dlength(value) {
            this.setValue("dlength", value)
        },
        get mark() {
            return this.getValue("mark");
        },
        set mark(value) {
            this.setValue("mark", value)
        },
        get animation() {
            return this.getValue("animation");
        },
        set animation(value) {
            if (this.setValue("animation", value)) {
                if (value) this.addPropertyAnimation(value, this);
            }
        },
        set transition(value) {
            this.setValue("transition", value);
        },
        get transition() {
            return this.getValue("transition");
        },
        set opacity(value) {
            this.setValue("opacity", value);
        },
        get opacity() {
            return this.getValue("opacity");
        }
    })
})